
/*    This file is part of the managarm operating system.
 *   Copyright (C) 2007, 2008, 2009  Alexander van der Grinten
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. */


import java.io.*;
import java.util.*;

import org.apache.tools.ant.Task;
import org.apache.tools.ant.BuildException;

public final class KoronaCompiler extends Task {
	private static class StreamProcessor implements Runnable {
		// source & target streams
		private InputStream source;
		private OutputStream target;
		
		public StreamProcessor(InputStream source, OutputStream target) {
			this.source = source;
			this.target = target;
		}
		
		// inherited from Runnable
		public void run() {
			while(true) {
				try {
					final int data = source.read();
					if(data == -1)
						return;
					target.write(data);
				}catch(IOException e) {
					System.err.println("Communication error");
				}
			}
		}
	}
	
	
	public final class NestedDefine {
		private String attrIdent;
		public NestedDefine() { nestedDefines.add(this); }
		public void setIdent(final String value) { attrIdent = value; }
	}
	public final class NestedHeader {
		private String attrFile;
		public NestedHeader() { nestedHeaders.add(this); }
		public void setFile(final String value) { attrFile = value; }
	}
	public final class NestedInclude {
		private String attrDirectory;
		public NestedInclude() { nestedIncludes.add(this); }
		public void setDir(final String value) { attrDirectory = value; }
	}
	public final class NestedImport {
		private String attrLibrary;
		private String attrDirectory;
		public NestedImport() { nestedImports.add(this); }
		public void setLib(final String value) { attrLibrary = value; }
		public void setDir(final String value) { attrDirectory = value; }
	}
	
	// list of nested include directives
	private LinkedList<NestedDefine> nestedDefines
			= new LinkedList<NestedDefine>();
	private LinkedList<NestedHeader> nestedHeaders
			= new LinkedList<NestedHeader>();
	private LinkedList<NestedInclude> nestedIncludes
			= new LinkedList<NestedInclude>();
	private LinkedList<NestedImport> nestedImports
			= new LinkedList<NestedImport>();
	
	// attributes specified in the build file
	private String attrSource;
	private String attrDest;
	
	// setter methods required by apache ant
	public void setSrc(final String value) { attrSource = value; }
	public void setDest(final String value) { attrDest = value; }
	
	// nested element creation methods
	public NestedDefine createDefine() { return new NestedDefine(); }
	public NestedHeader createHeader() { return new NestedHeader(); }
	public NestedInclude createInclude() { return new NestedInclude(); }
	public NestedImport createImport() { return new NestedImport(); }
	
	// execute() method required by apache ant
	public void execute() throws BuildException {
		// figure out the correct jkorc command
		final String command = getProject().getProperty
				("managarm.anttasks.commands.jkorc");
		final String directory = getProject().getProperty("basedir");

		try {
			final StringBuffer cmdline = new StringBuffer();
			cmdline.append(command);
			cmdline.append(" -base ");
			cmdline.append(directory);
			cmdline.append(" -source ");
			cmdline.append(attrSource);
			cmdline.append(" -dest ");
			cmdline.append(attrDest);
			
			// add define directives to the command line
			final Iterator<NestedDefine> defines = nestedDefines.iterator();
			while(defines.hasNext()) {
				final NestedDefine directive = defines.next();
				cmdline.append(" -define ");
				cmdline.append(directive.attrIdent);
			}
			
			// add header directives to the command line
			final Iterator<NestedHeader> headers = nestedHeaders.iterator();
			while(headers.hasNext()) {
				final NestedHeader directive = headers.next();
				cmdline.append(" -header ");
				cmdline.append(directive.attrFile);
			}
			
			// add include directives to the command line
			final Iterator<NestedInclude> includes = nestedIncludes.iterator();
			while(includes.hasNext()) {
				final NestedInclude directive = includes.next();
				cmdline.append(" -include ");
				cmdline.append(directive.attrDirectory);
			}
			
			// add import directives to the command line
			final Iterator<NestedImport> imports = nestedImports.iterator();
			while(imports.hasNext()) {
				final NestedImport directive = imports.next();
				cmdline.append(" -import ");
				cmdline.append(directive.attrLibrary);
				cmdline.append(" ");
				cmdline.append(directive.attrDirectory);
			}
			
			final Process process = Runtime.getRuntime().exec(cmdline.toString());
			final Thread stdproc = new Thread(new StreamProcessor
					(process.getInputStream(), System.out));
			final Thread errproc = new Thread(new StreamProcessor
					(process.getErrorStream(), System.err));
			stdproc.start();
			errproc.start();
			
			// wait until the process finishes
			process.waitFor();
			stdproc.join();
			errproc.join();
			
			if(process.exitValue() != 0)
				throw new BuildException("jkorc failed");
		} catch(InterruptedException e) {
			log("Interrupted while running jkorc");
			log(e.getMessage());
			throw new BuildException("Could not run jkorc");
		} catch(IOException e) {
			log("Could not invoke jkorc");
			log(e.getMessage());
			throw new BuildException("Could not run jkorc");
		}
	}
}

